package com.jar.exercise.多线程.线程回调;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.jar.exercise.多线程.线程回调.SmallTool.printTimeAndThread;
import static com.jar.exercise.多线程.线程回调.SmallTool.sleepMillis;

/**
 * CompletableFuture 例子
 */
@Slf4j
public class CompletableFutureDemo {

    @Test
    public void t001(){

        sleepMillis(1000);
        printTimeAndThread("二逼青年：进店");
        sleepMillis(1000);
        printTimeAndThread("二逼青年：点菜：青椒肉丝");

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                    sleepMillis(1000);
                    printTimeAndThread("厨师：做饭");
                    sleepMillis(1000);
                    printTimeAndThread("厨师：盛饭");
                    return "青椒肉丝";
                }).thenCombineAsync(CompletableFuture.supplyAsync(() -> {
                    sleepMillis(1000);
                    printTimeAndThread("服务员：蒸米饭");
                    sleepMillis(1000);
                    printTimeAndThread("服务员：搞定米饭");
                    return "米饭";
                }), (old, now) -> old + " 和 " + now)
                .thenApplyAsync(s -> {
                    sleepMillis(1000);
                    printTimeAndThread(String.format("服务员：拿到菜 %s", s));
                    sleepMillis(1000);
                    printTimeAndThread("服务员：服务员正在骑马赶来的路上");
                    sleepMillis(1000);
                    printTimeAndThread("服务员：送到8号桌");
                    return s;
                });


        CompletableFuture.allOf(
                CompletableFuture.supplyAsync(() -> {
                    sleepMillis(1000);
                    printTimeAndThread("厨师：做饭");
                    sleepMillis(1000);
                    printTimeAndThread("厨师：盛饭");
                    return "青椒肉丝";
                }),
                CompletableFuture.supplyAsync(() -> {
                    sleepMillis(1000);
                    printTimeAndThread("厨师：做饭");
                    sleepMillis(1000);
                    printTimeAndThread("厨师：盛饭");
                    return "青椒肉丝";
                })
        );


        sleepMillis(1000);
        printTimeAndThread("二逼青年：玩手机等上菜");
        sleepMillis(1000);
        printTimeAndThread(String.format("二逼青年：次饭:%s", future.join()));


    }

    @Test
    public void t2() throws ExecutionException, InterruptedException {
        CompletableFuture<String> completableFuture = new CompletableFuture<>();
        completableFuture.thenAccept(System.out::println);
        System.out.println(completableFuture.get());
    }

    /**
     *
     */
    @Test
    public void whenCompleted() throws InterruptedException {
        CompletableFuture<Integer> f = new CompletableFuture<>();

        new Thread(() -> {
            // 子线程A启动
            //log.info("子线程A启动");
            try {
                //log.info("子线程A沉睡5s");
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //log.info("子线程A令future完成");
            // 当子线程A执行到f.complete的时候，会去看是否有注册好的f的then或者when（非async的），如果有的话，会顺次去执行。
            f.complete(100);
            //log.info("子线程A结束");
            //log.info("Thread:当前线程{}", Thread.currentThread().getName());
        }).start();


        // 当前线程（主线程）执行到这里的时候，如果子线程还没有执行到f.complete(100)，
        // 那么当前线程会把whenComplete事件注册起来，并且说好哪个线程执行了f.complete(100)，
        // 哪个线程就负责执行whenComplete的内容。
        // 如果当前线程（主线程）执行到这里的时候，f.complete(100)已经被其他线程执行完毕了。
        //里面 那么只有当前线程自己来执行whenComplete的内容了。
        f.whenCompleteAsync((i, ex) -> {
            //log.info("whenComplete:当前线程{}", Thread.currentThread().getName());
            // 这个场景下，whenComplete的回调的执行线程会是子线程A
            //log.info("do something after complete begin");
            try {
                //log.info("沉睡10s");
                Thread.sleep(10000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //log.info("do something after complete end");
        });


        //log.info("main over");
        //log.info("Main:当前线程{}", Thread.currentThread().getName());

        Thread.currentThread().join();

    }

    @Test
    public void test3() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = new CompletableFuture<>();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            try {
                Thread.sleep(3000);
                future.complete("异步任务执行结果");
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });


        future.whenComplete((s, throwable) -> {
            System.out.println("当异步任务执行完毕时打印异步任务的执行结果: " + s);
        });

        CompletableFuture<Integer> integerFuture = future.thenApply(s -> {
            System.out.println("当异步任务执行结束时, 根据上一次的异步任务结果, 继续开始一个新的异步任务!");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return s.length();
        });

        System.out.println("阻塞方式获取执行结果:" + integerFuture.get());
        System.out.println("阻塞方式获取执行结果:" + future.get());
        while (Thread.activeCount() > 1) {
            Thread.yield();
        }
    }

    @Test
    public void test4() throws InterruptedException, ExecutionException {
        CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println("师傅准备做蛋糕 begin");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cake";
        }).thenApply((cake) -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println("师傅准备做蛋糕 thenApply :" + cake);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return cake.length();
        }).thenAccept(cake -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println("师傅准备做蛋糕 thenAccept :长度：" + cake);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread.currentThread().join();

    }

    @Test
    public void t1() {

        String mobile = "15137075637";

        final Map<String, String> stringStringMap = sendMessageCode("15137075637");
        final String msg = stringStringMap.get("msg");

        final CompletableFuture<Void> voidCompletableFuture = CompletableFuture
                .supplyAsync(() -> login(mobile, msg))
                .thenAcceptAsync(stringStringMap1 -> {
                    final String code = stringStringMap.get("code");

                    if (Objects.equals(code, "200")) {
                        delCode(mobile);
                    }

                    System.out.println("mobile : " + map.get(mobile));
                });


        CompletableFuture.allOf(voidCompletableFuture);

    }

    /**
     * 缓存验证码和手机号
     */
    Map<String, String> map = new HashMap<>();

    /**
     * 发送验证码
     *
     * @param mobile 发送验证码的手机号码
     * @return 结果
     */
    public Map<String, String> sendMessageCode(String mobile) {

        final String s1 = String.valueOf(new Random().nextInt(90000) + 10000);

        cacheCode(mobile, s1);
        System.out.println("生成验证码 ： " + s1);
        return ok(s1);
    }

    /**
     * 模拟登陆
     *
     * @param mobile 手机号码
     * @param code   验证码
     * @return 结果
     */
    public Map<String, String> login(String mobile, String code) {
        System.out.println("登陆验证码 ： " + code);

        if (Objects.equals(code, map.get(mobile))) {
            return ok("ok");
        }

        return fail();
    }

    /**
     * 删除手机号对应的 验证码值
     *
     * @param mobile 手机号码
     * @return 结果
     */
    public String delCode(String mobile) {
        System.out.println("删除验证码");
        return map.remove(mobile);
    }


    /**
     * 删除手机号对应的 验证码值
     *
     * @param mobile 手机号码
     * @param code   验证码
     * @return 结果
     */
    public String cacheCode(String mobile, String code) {
        return map.put(mobile, code);
    }


    public Map<String, String> ok(String msg) {
        Map<String, String> map = new HashMap<>(4);
        map.put("code", "200");
        map.put("msg", msg);
        return map;
    }

    public Map<String, String> fail() {
        Map<String, String> map = new HashMap<>(4);
        map.put("code", "500");
        map.put("msg", "fail");
        return map;
    }

}
